package io.cici.cc.mybatis.lite.session.defaults;

import io.cici.cc.mybatis.lite.binding.BindingException;
import io.cici.cc.mybatis.lite.exceptions.ExceptionFactory;
import io.cici.cc.mybatis.lite.exceptions.TooManyResultsException;
import io.cici.cc.mybatis.lite.executor.BatchResult;
import io.cici.cc.mybatis.lite.executor.Executor;
import io.cici.cc.mybatis.lite.executor.resultset.ResultContextHandler;
import io.cici.cc.mybatis.lite.mapping.MappedStatement;
import io.cici.cc.mybatis.lite.reflection.ParameterResolver;
import io.cici.cc.mybatis.lite.session.Configuration;
import io.cici.cc.mybatis.lite.session.Session;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;

public class SessionImpl implements Session {

    private final Configuration configuration;
    private final Executor executor;

    private final boolean autoCommit;


    public SessionImpl(Configuration configuration, Executor executor, boolean autoCommit) {
        this.configuration = configuration;
        this.executor = executor;

        this.autoCommit = autoCommit;
    }

    public SessionImpl(Configuration configuration, Executor executor) {
        this(configuration, executor, false);
    }

    @Override
    public <T> T selectOne(String statement) {
        return this.selectOne(statement, null);
    }

    @Override
    public <T> T selectOne(String statement, Object parameter) {
        // 期望返回0条或者1条记录,但是返回N条记录,建议在查询语句增加limit 1或者添加唯一索引
        List<T> list = this.selectList(statement, parameter);
        if (list.size() == 0) {
            return null;
        } else if (list.size() == 1) {
            return list.get(0);
        } else {
            throw new TooManyResultsException("期望返回0条或者1条记录,但是返回:" + list.size() + "条记录");
        }
    }

    @Override
    public <E> List<E> selectList(String statement) {
        return this.selectList(statement, null);
    }

    @Override
    public <E> List<E> selectList(String statement, Object parameter) {
        return selectList(statement, parameter, Executor.NO_RESULT_HANDLER);
    }

    private <E> List<E> selectList(String statement, Object parameter, ResultContextHandler handler) {
        try {
            MappedStatement mappedStatement = configuration.getMappedStatement(statement);

            return executor.query(mappedStatement, wrapCollection(parameter), handler);
        } catch (Exception e) {
            throw ExceptionFactory.wrapException(e);
        } finally {

        }
    }

    @Override
    public void select(String statement, ResultContextHandler handler) {
        select(statement, null, handler);
    }

    @Override
    public void select(String statement, Object parameter, ResultContextHandler handler) {
        selectList(statement, parameter, handler);
    }

    @Override
    public int insert(String statement) {
        return insert(statement, null);
    }

    @Override
    public int insert(String statement, Object parameter) {
        return update(statement, parameter);
    }

    @Override
    public int update(String statement) {
        return update(statement, null);
    }

    @Override
    public int update(String statement, Object parameter) {
        try {

            MappedStatement mappedStatement = configuration.getMappedStatement(statement);
            return executor.update(mappedStatement, wrapCollection(parameter));
        } catch (Exception e) {
            throw ExceptionFactory.wrapException(e);
        } finally {

        }
    }

    @Override
    public int delete(String statement) {
        return update(statement, null);
    }

    @Override
    public int delete(String statement, Object parameter) {
        return update(statement, parameter);
    }

    @Override
    public void commit() {
        commit(false);
    }

    @Override
    public void commit(boolean force) {
        try {
            executor.commit(isCommitOrRollbackRequired(force));

        } catch (Exception e) {
            throw ExceptionFactory.wrapException(e);
        } finally {

        }
    }

    @Override
    public void rollback() {
        rollback(false);
    }

    @Override
    public void rollback(boolean force) {
        try {
            executor.rollback(isCommitOrRollbackRequired(force));

        } catch (Exception e) {
            throw ExceptionFactory.wrapException(e);
        } finally {

        }
    }

    @Override
    public List<BatchResult> flushStatements() {
        try {
            return executor.flushStatements();
        } catch (Exception e) {
            throw ExceptionFactory.wrapException(e);
        } finally {

        }
    }

    @Override
    public void close() {
        try {
            executor.close(isCommitOrRollbackRequired(false));


        } finally {

        }
    }

    @Override
    public Configuration getConfiguration() {
        return configuration;
    }

    @Override
    public <T> T getMapper(Class<T> type) {
        return configuration.getMapper(type, this);
    }

    @Override
    public Connection getConnection() {
        try {
            return executor.getTransaction().getConnection();
        } catch (SQLException e) {
            throw ExceptionFactory.wrapException(e);
        }
    }

    private boolean isCommitOrRollbackRequired(boolean force) {
        return !autoCommit || force;
    }

    private Object wrapCollection(final Object object) {
        return ParameterResolver.wrapToMapIfCollection(object, null);
    }


    public static class StrictMap<V> extends HashMap<String, V> {

        private static final long serialVersionUID = -5741767162221585340L;

        @Override
        public V get(Object key) {
            if (!super.containsKey(key)) {
                throw new BindingException(
                        "Parameter '" + key + "' not found. Available parameters are " + this.keySet());
            }
            return super.get(key);
        }

    }

}
